// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


`include "espi_header.iv"

`timescale 1 ps / 1 ps
module espi_oob_channel #(
    parameter DEVICE_FAMILY      = "MAX 10 FPGA",
    parameter OOB_TXFIFO_SIZE     = 2048,
    parameter OOB_TXFIFO_WIDTHU   = 11,
    parameter OOB_RXFIFO_SIZE     = 2048,
    parameter OOB_RXFIFO_WIDTHU   = 11,
    parameter HDRBYTE_ARR        = 3,
    parameter DATABYTE_ARR       = 2
)(
    input                             clk,
    input                             reset_n,
    input                             pec_mismatch,
    input                             oob_error_condition,
    input                             flush_oob_fifo,
    input                             oob_channel_reset,
    input                             stop_det,
    input                             oob_channel_en,
    input                             read_oob_rxfifo,
    input [7:0]                       txfifo_wdata,
    input [1:0]                       rx_hdr_array_cnt,
    input [2:0]                       rx_data_array_cnt,
    input [7:0]                       header_byte[HDRBYTE_ARR],
    input [7:0]                       data_byte[DATABYTE_ARR],
    input [7:0]                       command_byte,
    input                             rx_detect_data,
    input                             rx_detect_header,
    input                             rx_detect_header_end,
    input                             rx_detect_data_end,
    input                             rx_detect_crc_end,
    input                             rx_detect_command,
    input                             tx_gen_command,
    input                             tx_gen_header,
    input                             tx_gen_data,
    input                             spiclk_cnt_done_hl,
    input                             spiclk_cnt_done_lh,
    input                             detect_put_oob_rxfifo,
    input                             detect_get_oob_txfifo,
    input                             write_oob_txfifo,
    output logic                      oob_free,
    output logic                      oob_rxfifo_avail,
    output logic                      oob_channel_ready,
    output logic [7:0]                oob_rxfifo_rdata,
    output logic [7:0]                oob_txfifo_rdata,
    output logic                      get_oob_txfifo,
    output logic                      oob_txfifo_empty
);

logic channel_reset_n, put_oob_rxfifo, get_oob_rxfifo;
logic oob_rxfifo_full, oob_rxfifo_empty, oob_txfifo_full;
logic [7:0] oob_rxfifo_wdata, txfifo_rdata;

assign oob_txfifo_rdata = detect_get_oob_txfifo ? txfifo_rdata :
                           {8{1'b0}};
assign channel_reset_n = reset_n && ~oob_channel_reset;
assign put_oob_rxfifo = (detect_put_oob_rxfifo && spiclk_cnt_done_hl && (rx_detect_command || rx_detect_header || rx_detect_data));
assign oob_rxfifo_wdata = (rx_detect_command && spiclk_cnt_done_hl) ? command_byte :
                           (rx_detect_header && spiclk_cnt_done_hl) ? header_byte[rx_hdr_array_cnt] :
                             (rx_detect_data && spiclk_cnt_done_hl) ? data_byte[rx_data_array_cnt] : {8{1'b0}};

always @(posedge clk or negedge channel_reset_n) begin
    if (!channel_reset_n) begin
        oob_free  <= 1'b1;           // during reset, empty is high, hence oob_free is 1
    end
    else begin
        if (oob_rxfifo_empty) begin
            oob_free <= 1'b1;
        end
        else if (oob_rxfifo_avail) begin
            oob_free <= 1'b0;
        end
    end
end

always @(posedge clk or negedge channel_reset_n) begin
    if (!channel_reset_n) begin
        oob_rxfifo_avail  <= 1'b0;
    end
    else begin
        if (detect_put_oob_rxfifo && rx_detect_crc_end && ~oob_error_condition && ~pec_mismatch) begin
            oob_rxfifo_avail <= 1'b1;
        end
        else if (oob_rxfifo_empty) begin
            oob_rxfifo_avail <= 1'b0;
        end
    end
end

always @(posedge clk or negedge channel_reset_n) begin
    if (!channel_reset_n) begin
        oob_channel_ready  <= 1'b0;
    end
    else begin
        oob_channel_ready <= oob_channel_en;
    end
end

always @(posedge clk or negedge channel_reset_n) begin
    if (!channel_reset_n) begin
        get_oob_rxfifo  <= 1'b0;
    end
    else begin
        if (read_oob_rxfifo) begin
            get_oob_rxfifo  <= 1'b1;
        end
        else begin
            get_oob_rxfifo  <= 1'b0;
        end
    end
end

assign get_oob_txfifo = (detect_get_oob_txfifo && spiclk_cnt_done_lh &&
                         (tx_gen_command || tx_gen_header || tx_gen_data) &&
                         ~oob_txfifo_empty);

espi_fifo #(
    .DSIZE  (8),
    .DEPTH  (OOB_RXFIFO_SIZE),
    .WIDTHU (OOB_RXFIFO_WIDTHU),
    .FAMILY (DEVICE_FAMILY)
) oob_rxfifo_inst (
    .clk          (clk),
    .rst_n        (channel_reset_n && ~flush_oob_fifo),
    .put          (put_oob_rxfifo),
    .get          (get_oob_rxfifo),
    .wdata        (oob_rxfifo_wdata),
    .full         (oob_rxfifo_full),
    .empty        (oob_rxfifo_empty),
    .rdata        (oob_rxfifo_rdata),
    .usedw        ()
);

espi_fifo #(
    .DSIZE  (8),
    .DEPTH  (OOB_TXFIFO_SIZE),
    .WIDTHU (OOB_TXFIFO_WIDTHU),
    .FAMILY (DEVICE_FAMILY)
) oob_txfifo_inst (
    .clk          (clk),
    .rst_n        (channel_reset_n),
    .put          (write_oob_txfifo),
    .get          (get_oob_txfifo),
    .wdata        (txfifo_wdata),
    .full         (oob_txfifo_full),
    .empty        (oob_txfifo_empty),
    .rdata        (txfifo_rdata),
    .usedw        ()
);

endmodule
